package q51_solveNQueens;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    List<List<String>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        // 首先设置一个完整的棋盘 用于存放棋子和判断
        for (char[] c : chessboard) {
            Arrays.fill(c, '.');
        }
        // 参数包括n以及行号，以及棋盘
        backTrack(n, 0, chessboard);
        return res;
    }

    /**
     * 在该dfs中，我们每次的更改是基于棋盘数组进行的
     * 整体来看 仍然符合之前的模版式架构
     * 1 判断终止条件 当我们遍历的行到达最后时，也就可以返回
     * 2 遍历体中 先判断是否有效 然后放入棋子 继续dfs 然后消除棋子
     * @param n 棋盘长
     * @param row 行
     * @param chessboard 棋盘
     */
    public void backTrack(int n, int row, char[][] chessboard) {
        if (row == n) {
            res.add(Array2List(chessboard));
            return;
        }

        // 这里需要注意的是 如果我们选择以 行 作为每次dfs的参数
        // 那么在这一层我们就要遍历所有的 列 进行判断
        // 并且每次我们传入的行就要 +1 也就是进入下一行的摆放
        for (int col = 0;col < n; ++col) {
            // 这里的判断方法isValid意义是
            // 假设我们在当前位置放一个Q 是否合法
            // 如果合法 再放入
            if (isValid(row, col, n, chessboard)) {
                chessboard[row][col] = 'Q';
                backTrack(n, row + 1, chessboard);
                chessboard[row][col] = '.';
            }
        }

    }


    /**
     * 这里专门使用一个转换的函数来帮助我们构建答案
     * 因为在res中可能存在多种棋盘摆放方式
     */
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>();

        for (char[] c : chessboard) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }


    /**
     * 判断有效 皇后的攻击判断需要包含行、列、45度斜角
     * 所以需要判断三种情况
     * @param row 行
     * @param col 列
     * @param n 长款
     * @param chessboard 棋盘
     * @return 是否有效
     */
    public boolean isValid(int row, int col, int n, char[][] chessboard) {
        // 检查列
        for (int i=0; i<row; ++i) { // 相当于剪枝
            if (chessboard[i][col] == 'Q') {
                return false;
            }
        }

        // 检查45度对角线
        for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }

        // 检查135度对角线
        for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }
}
